package com.ai.networking.websocket.service;

import com.ai.networking.websocket.service.comm.CommonWebSocket;
import com.ai.networking.websocket.service.entity.WSMessage;
import com.ai.networking.websocket.service.util.MessageDecoder;
import com.ai.networking.websocket.service.util.MessageEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

@Slf4j
//@Component
@ServerEndpoint(value = "/ws", decoders = MessageDecoder.class, encoders = MessageEncoder.class)
public class WebSocket {

    /**
     * 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;

    private String wsType = null;

    private Integer wsCode = null;

    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(Session session) {
        session.setMaxBinaryMessageBufferSize(1024 * 1024 * 5);
        session.setMaxTextMessageBufferSize(1024 * 1024 * 5);

        this.session = session;
        CommonWebSocket.WS_SOCKET_SET.add(this);

        // 在线数加1,
        addNumber();

        log.info("有新用户连接，当前用户总数为：" + getNumber());

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        if (wsType == null) {
            wsType = "";
        }
        switch (wsType) {
            case "live":
                CommonWebSocket.WS_LIVE_MAP.get(wsCode).remove(this);
            default:
                CommonWebSocket.WS_SOCKET_SET.remove(this);
                break;
        }
        subNumber();
        log.info("有用户下线，当前用户总数为：" + getNumber());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param wsMessage 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(WSMessage wsMessage) {
        log.info("[websocket server] 收到消息：" + wsMessage);
        if (wsType == null) {
            wsType = "";
        }
        switch (wsType) {
            case "live":
                CommonWebSocket.WS_LIVE_MAP.get(wsCode).remove(this);
                break;
            default:
                CommonWebSocket.WS_SOCKET_SET.remove(this);
                break;
        }
        wsType = wsMessage.getType() == null ? "" : wsMessage.getType();
        wsCode = wsMessage.getCode() == null ? 0 : wsMessage.getCode();
        CopyOnWriteArraySet<WebSocket> set = new CopyOnWriteArraySet<>();
        switch (wsType) {
            case "live":
                if (CommonWebSocket.WS_LIVE_MAP.containsKey(wsCode)) {
                    CommonWebSocket.WS_LIVE_MAP.get(wsCode).add(this);
                } else {
                    set.add(this);
                    CommonWebSocket.WS_LIVE_MAP.put(wsCode, set);
                }
                break;
            default:
                CommonWebSocket.WS_SOCKET_SET.add(this);
                break;
        }

        sendMessage(wsMessage);
    }

    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Throwable error) {
        log.error(error.toString());
    }

    /**
     * 发送对象到客户端
     */
    public void sendMessage(WSMessage wsMessage) {
        try {
            this.session.getBasicRemote().sendObject(wsMessage);
        } catch (EncodeException | IOException e) {
            log.error("发送消息错误：" + wsMessage);
//            e.printStackTrace();
        }
    }


    /**
     * 发送文本到客户端
     */
    public void sendMessage(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
            log.info("发送消息成功：" + message);
        } catch (IOException e) {
            log.error("发送消息错误：" + message);
//            e.printStackTrace();
        }
    }


    /**
     * 静态变量，用来记录当前人员在线连接数。应该把它设计成线程安全的。
     */
    private static int number = 0;

    private static synchronized void addNumber() {
        number++;
    }


    private static synchronized void subNumber() {
        number--;
    }


    private static synchronized int getNumber() {
        return number;
    }
}
